package com.xncoding.trans.service;

import com.xncoding.trans.dao.entity.User;
import com.xncoding.trans.dao.repository.UserMapper;
import com.xncoding.trans.exception.MyException;
import com.xncoding.trans.service.impl.IUserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.support.TransactionTemplate;

import javax.annotation.Resource;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

@Service
public class UserServiceImpl implements IUserService {
    @Resource
    private UserMapper userMapper;
    @Autowired
    private TransactionTemplate transactionTemplate;

    private Lock lock = new ReentrantLock();

    @Override
    public User getById(int id) {
        return userMapper.selectById(id);
    }

    // 1、外层加事务，内层不加事务，内层抛异常会不会回滚【会】
    @Transactional
    public void updateUserById(Integer id){
        User user = userMapper.selectById(id);
        user.setUsername("test-");
        updateUser(user);

    }

    public void updateUser(User user) {
        userMapper.updateById(user);
        throw new MyException("外层加事务，内层不加事务，内层抛异常会不会回滚。。。");
    }

    // 2、外层加事务，内层加事务，内层抛异常会不会回滚【会】
    @Transactional
    public void updateUserById1(Integer id){
        User user = userMapper.selectById(id);
        user.setUsername("test-transactional-");
        updateUserWithTransaction(user);

    }

    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public void updateUserWithTransaction(User user) {
        userMapper.updateById(user);
        throw new MyException("外层加事务，内层加事务，内层抛异常会不会回滚。。。");
    }

    // 3、外层不加事务，内层加事务，内层抛异常会不会回滚【不会】
    public void updateUserById2(Integer id){
        User user = userMapper.selectById(id);
        user.setUsername("test-transactional-2");
    }

    public void updateUserById3(){
        User user = userMapper.selectById(1);

    }

    /**
     * 增删改要写 ReadOnly=false 为可写
     * @param user 用户
     */
    @Transactional(readOnly = false)
    public void updateUserError(User user) {
        userMapper.updateById(user);
        errMethod(); // 执行一个会抛出异常的方法
    }

    @Transactional(readOnly = false, noRollbackFor = {MyException.class})
    public void updateUserError2(User user) {
        userMapper.updateById(user);
        errMethod2(); // 执行一个会抛出自定义异常的方法
    }

    @Autowired
    private TransactionTemplate template;

    public void getUserBy(String name){
        try {
            if (lock.tryLock(10, TimeUnit.SECONDS)){
                System.out.println(Thread.currentThread().getName() + " 获取到锁");
                try{
                    commit(name);
                }finally {
                    lock.unlock();
                    System.out.println(Thread.currentThread().getName() + " 解锁");
                }
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    @Transactional
    public void commit(String name){
//        template.executeWithoutResult((transactionStatus) -> {
            User user = userMapper.selectById(1);
            System.out.println(Thread.currentThread().getName() + " ---> " + user);
            if(!user.getPassword().endsWith(name)){
                System.out.println(Thread.currentThread().getName() + " 更新user");
                user.setPassword("4312"+name);
                userMapper.updateById(user);
            }
//                    Integer insert = userMapper.insert(new User("name", "password"));
//        });
    }

//    @Transactional
    public synchronized void getUser1(String name){
        getUserWithTransaction(name);
    }

    @Transactional
    public synchronized Integer getUserWithTransaction(String name){
        Integer ret = 0;
//        try {
//            if (lock.tryLock(10, TimeUnit.SECONDS)){
                System.out.println(Thread.currentThread().getName() + " 获取到锁");
                try{
                    User user = userMapper.selectById(1);
                    long current = System.currentTimeMillis();
                    System.out.println(Thread.currentThread().getName() + " ---> " + user);
                    if(current - user.getTime() > 1000){
                        System.out.println(Thread.currentThread().getName() + " 更新user");
                        user.setPassword("test_123_"+name);
                        user.setTime(current);
                        userMapper.updateById(user);
                        ret = 1;
                    }
                    if (user.getPassword().contains(name)){
//                        throw new RuntimeException("该回滚了...");
                    }
//                    Integer insert = userMapper.insert(new User("name", "password"));
                    return ret;
                } catch (Exception e) {
                    System.out.println("该回滚了。。。");
//                    TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
                } finally {
//                    lock.unlock();
                    System.out.println(Thread.currentThread().getName() + " 解锁");
                }
//            }
//        } catch (InterruptedException e) {
//            e.printStackTrace();
//        }
        return null;
    }

    @Transactional
    public Integer getUserWithTransaction2(String name){
        Integer ret = 0;
        try {
            if (lock.tryLock(10, TimeUnit.SECONDS)){
                System.out.println(Thread.currentThread().getName() + " 获取到锁");
                try{
                    User user = userMapper.selectById(1);
                    long current = System.currentTimeMillis();
                    System.out.println(Thread.currentThread().getName() + " ---> " + user);
                    if(current - user.getTime() > 1000){
                        ret = doGetUserWithTransaction(name, user, current);
                    }
                    if (user.getPassword().contains(name)){
        //                        throw new RuntimeException("该回滚了...");
                    }
                    return ret;
                } catch (Exception e) {
                    System.out.println("该回滚了。。。");
                } finally {
                    lock.unlock();
                    System.out.println(Thread.currentThread().getName() + " 解锁");
                }
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return null;
    }

    public Integer doGetUserWithTransaction(String name, User user, long current) {
        return transactionTemplate.execute(transactionStatus -> {
            try{
                // todo insert into logs values()
                user.setPassword("test_123_" + name);
                user.setTime(current);
                return userMapper.updateById(user);
            }catch (Exception e ){
                transactionStatus.setRollbackOnly();
            }
            return 0;
        });
    }

    private void errMethod() {
        System.out.println("error");
//        throw new RuntimeException("runtime");
    }
    private void errMethod2() {
        System.out.println("error");
        throw new MyException("runtime");
    }
}
